///////////////////////////////////////////////////////////////////////////////
//
// DBT86 Project : x86 Platform : x86 Interpreter : lazy condition flags
// Copyright (C) 2004 by Ben Hertzberg and Stanford University
// All Rights Reserved
//
// This code may not be used for any purpose without the express written
// consent of the author.  See the LICENSE.txt file for details.
//
//
// DESCRIPTION
//
// This unit implements lazy condition flag evaluation on x86.
// x86 has many operations that set multiple flags, and computing the
// actual values of those flags would slow things down.  Instead we
// save off values necessary to construct the flags later.  Only the
// specific flags requested are evaluated, and optimized paths are in
// place to deal with common condition-evaluation constructs.
//
// NOTE: assumes X86_CTXT(flagsSrc1),X86_CTXT(flagsSrc2),X86_CTXT(flagsSrc3) are unsigned (?)
//
// TODO: X64 support
// TODO: shifts, rotates, mul, div, lahf, other weird ops
///////////////////////////////////////////////////////////////////////////////

#define INCL_FLAGS_LOCALS
#include "interp.h"

static BYTE ParityTable[256] = { 
  1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
  0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
  0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
  1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
  0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
  1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
  1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
  0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
  0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
  1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
  1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
  0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
  1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
  0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
  0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
  1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
};

static IVAL computeFlagsResultValue() {
  switch (X86_CTXT(flagsOp)) {
    case CF_NOT_LAZY:
      ASSERT(UNREACHED);
      return 0;
    case CF_BCD:
      return X86_CTXT(flagsSrc2) & 0xFF;
    case CF_ADD_BYTE:
    case CF_INC_BYTE:
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2)) & 0xFF;
    case CF_ADD_WORD:
    case CF_INC_WORD:
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2)) & 0xFFFF;
    case CF_ADD_DWORD:
    case CF_INC_DWORD:
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2)) & 0xFFFFFFFF;
    case CF_OR_BYTE:
      return (X86_CTXT(flagsSrc1) | X86_CTXT(flagsSrc2)) & 0xFF;
    case CF_OR_WORD:
      return (X86_CTXT(flagsSrc1) | X86_CTXT(flagsSrc2)) & 0xFFFF;
    case CF_OR_DWORD:
      return (X86_CTXT(flagsSrc1) | X86_CTXT(flagsSrc2)) & 0xFFFFFFFF;
    case CF_ADC_BYTE:
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0)) & 0xFF;
    case CF_ADC_WORD:
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0)) & 0xFFFF;
    case CF_ADC_DWORD:
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0)) & 0xFFFFFFFF;
    case CF_SBB_BYTE:
      return (X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2) -(X86_CTXT(flagsSrc3) != 0)) & 0xFF;
    case CF_SBB_WORD:
      return (X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2) -(X86_CTXT(flagsSrc3) != 0)) & 0xFFFF;
    case CF_SBB_DWORD:
      return (X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2) -(X86_CTXT(flagsSrc3) != 0)) & 0xFFFFFFFF;
    case CF_AND_BYTE:
      return (X86_CTXT(flagsSrc1) & X86_CTXT(flagsSrc2)) & 0xFF;
    case CF_AND_WORD:
      return (X86_CTXT(flagsSrc1) & X86_CTXT(flagsSrc2)) & 0xFFFF;
    case CF_AND_DWORD:
      return (X86_CTXT(flagsSrc1) & X86_CTXT(flagsSrc2)) & 0xFFFFFFFF;
    case CF_SUB_BYTE:
    case CF_DEC_BYTE:
      return (X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2)) & 0xFF;
    case CF_SUB_WORD:
    case CF_DEC_WORD:
      return (X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2)) & 0xFFFF;
    case CF_SUB_DWORD:
    case CF_DEC_DWORD:
      return (X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2)) & 0xFFFFFFFF;
    case CF_XOR_BYTE:
      return (X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2)) & 0xFF;
    case CF_XOR_WORD:
      return (X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2)) & 0xFFFF;
    case CF_XOR_DWORD:
      return (X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2)) & 0xFFFFFFFF;
    case CF_NEG_BYTE:
      return (0-X86_CTXT(flagsSrc1)) & 0xFF;
    case CF_NEG_WORD:
      return (0-X86_CTXT(flagsSrc1)) & 0xFFFF;
    case CF_NEG_DWORD:
      return (0-X86_CTXT(flagsSrc1)) & 0xFFFFFFFF;
    case CF_SHL_BYTE:
      return (X86_CTXT(flagsSrc1) << X86_CTXT(flagsSrc2)) & 0xFF;
    case CF_SHL_WORD:
      return (X86_CTXT(flagsSrc1) << X86_CTXT(flagsSrc2)) & 0xFFFF;
    case CF_SHL_DWORD:
      return (X86_CTXT(flagsSrc1) << X86_CTXT(flagsSrc2)) & 0xFFFFFFFF;
    case CF_SHR_BYTE:
      return (X86_CTXT(flagsSrc1) >> X86_CTXT(flagsSrc2)) & 0xFF;
    case CF_SHR_WORD:
      return (X86_CTXT(flagsSrc1) >> X86_CTXT(flagsSrc2)) & 0xFFFF;
    case CF_SHR_DWORD:
      return (X86_CTXT(flagsSrc1) >> X86_CTXT(flagsSrc2)) & 0xFFFFFFFF;
    case CF_SAR_BYTE:
      return (((INT8)X86_CTXT(flagsSrc1)) >> X86_CTXT(flagsSrc2)) & 0xFF;
    case CF_SAR_WORD:
      return (((INT16)X86_CTXT(flagsSrc1)) >> X86_CTXT(flagsSrc2)) & 0xFFFF;
    case CF_SAR_DWORD:
      return (((INT32)X86_CTXT(flagsSrc1)) >> X86_CTXT(flagsSrc2)) & 0xFFFFFFFF;
    default:
      ASSERT(UNREACHED);
      return 0;
  }
}

// The carry flag is one of the more commonly-used flags
IVAL getCarryFlag() {
#if CFG_64BIT_REGVALS
#else
  IVAL tmp;
#endif
  switch (X86_CTXT(flagsOp)) {
    case CF_NOT_LAZY:
    case CF_BCD:
      return X86_CTXT(flagsSrc1) & EFLAGS_BIT_C;
    case CF_ADD_BYTE:
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2)) & 0x100;
    case CF_ADD_WORD:
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2)) & 0x10000;
    case CF_ADD_DWORD:
#if CFG_64BIT_REGVALS
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2)) & 0x100000000ULL;
#else
      return X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) < X86_CTXT(flagsSrc1);
#endif
    case CF_ADC_BYTE:
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0)) & 0x100;
    case CF_ADC_WORD:
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0)) & 0x10000;
    case CF_ADC_DWORD:
#if CFG_64BIT_REGVALS
      return (X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0)) & 0x100000000ULL;
#else
      tmp = X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0);
      return (tmp < X86_CTXT(flagsSrc1)) || (X86_CTXT(flagsSrc3) && (tmp == X86_CTXT(flagsSrc1)));
#endif
    case CF_SBB_BYTE:
    case CF_SBB_WORD:
    case CF_SBB_DWORD:
      return X86_CTXT(flagsSrc1) < (X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0));

    case CF_SUB_BYTE:
    case CF_SUB_WORD:
    case CF_SUB_DWORD:
      return X86_CTXT(flagsSrc1) < X86_CTXT(flagsSrc2);

    case CF_NEG_BYTE:
    case CF_NEG_WORD:
    case CF_NEG_DWORD:
      return X86_CTXT(flagsSrc1) != 0; // 0 < X86_CTXT(flagsSrc1) equivalent since unsigned

    case CF_SHL_BYTE:
    case CF_SHL_WORD:
    case CF_SHL_DWORD:
      return (X86_CTXT(flagsSrc1) >> (32 - (X86_CTXT(flagsSrc2) & 0x1F))) & 0x1;
    case CF_SHR_BYTE:
    case CF_SHR_WORD:
    case CF_SHR_DWORD:
    case CF_SAR_BYTE:
    case CF_SAR_WORD:
    case CF_SAR_DWORD:
      return (X86_CTXT(flagsSrc1) >> ((X86_CTXT(flagsSrc2) & 0x1F) - 1)) & 0x1;

    case CF_OR_BYTE:
    case CF_OR_WORD:
    case CF_OR_DWORD:
    case CF_AND_BYTE:
    case CF_AND_WORD:
    case CF_AND_DWORD:
    case CF_XOR_BYTE:
    case CF_XOR_WORD:
    case CF_XOR_DWORD:
      return 0;

    case CF_INC_BYTE:
    case CF_INC_WORD:
    case CF_INC_DWORD:
    case CF_DEC_BYTE:
    case CF_DEC_WORD:
    case CF_DEC_DWORD:
      // inc and dec pass through the carry value
      return X86_CTXT(flagsSrc3);

    default:
      ASSERT(UNREACHED);
      return 0;
  }
}

// the overflow flag is used moderately, but mainly in conditional evaluations
// note that most conditional evaluations can avoid this path, though
IVAL getOverflowFlag() {
  switch (X86_CTXT(flagsOp)) {
    case CF_NOT_LAZY:
      return ((X86_CTXT(flagsSrc1) & EFLAGS_BIT_O) != 0);
    case CF_ADD_BYTE:
      return ((~(X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2))) & ((X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2)) ^ X86_CTXT(flagsSrc1)) & 0x80) != 0;
    case CF_ADD_WORD:
      return ((~(X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2))) & ((X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2)) ^ X86_CTXT(flagsSrc1)) & 0x8000) != 0;
    case CF_ADD_DWORD:
      return ((~(X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2))) & ((X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2)) ^ X86_CTXT(flagsSrc1)) & 0x80000000) != 0;
    case CF_ADC_BYTE:
      return ((~(X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2))) & ((X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0)) ^ X86_CTXT(flagsSrc1)) & 0x80) != 0;
    case CF_ADC_WORD:
      return ((~(X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2))) & ((X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0)) ^ X86_CTXT(flagsSrc1)) & 0x8000) != 0;
    case CF_ADC_DWORD:
      return ((~(X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2))) & ((X86_CTXT(flagsSrc1) + X86_CTXT(flagsSrc2) + (X86_CTXT(flagsSrc3) != 0)) ^ X86_CTXT(flagsSrc1)) & 0x80000000) != 0;
    case CF_SBB_BYTE:
      return ((X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2)) & ((X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2) - (X86_CTXT(flagsSrc3) != 0)) ^ X86_CTXT(flagsSrc1)) & 0x80) != 0;
    case CF_SBB_WORD:
      return ((X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2)) & ((X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2) - (X86_CTXT(flagsSrc3) != 0)) ^ X86_CTXT(flagsSrc1)) & 0x8000) != 0;
    case CF_SBB_DWORD:
      return ((X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2)) & ((X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2) - (X86_CTXT(flagsSrc3) != 0)) ^ X86_CTXT(flagsSrc1)) & 0x80000000) != 0;
    case CF_SUB_BYTE:
      return ((X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2)) & ((X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2)) ^ X86_CTXT(flagsSrc1)) & 0x80) != 0;
    case CF_SUB_WORD:
      return ((X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2)) & ((X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2)) ^ X86_CTXT(flagsSrc1)) & 0x8000) != 0;
    case CF_SUB_DWORD:
      return ((X86_CTXT(flagsSrc1) ^ X86_CTXT(flagsSrc2)) & ((X86_CTXT(flagsSrc1) - X86_CTXT(flagsSrc2)) ^ X86_CTXT(flagsSrc1)) & 0x80000000) != 0;

    case CF_NEG_BYTE:
      return (X86_CTXT(flagsSrc1) == 0x80);
    case CF_NEG_WORD:
      return (X86_CTXT(flagsSrc1) == 0x8000);
    case CF_NEG_DWORD:
      return (X86_CTXT(flagsSrc1) == 0x80000000);
    case CF_INC_BYTE:
      return (X86_CTXT(flagsSrc1) == 0x7F);
    case CF_INC_WORD:
      return (X86_CTXT(flagsSrc1) == 0x7FFF);
    case CF_INC_DWORD:
      return (X86_CTXT(flagsSrc1) == 0x7FFFFFFF);
    case CF_DEC_BYTE:
      return (X86_CTXT(flagsSrc1) == 0x80);
    case CF_DEC_WORD:
      return (X86_CTXT(flagsSrc1) == 0x8000);
    case CF_DEC_DWORD:
      return (X86_CTXT(flagsSrc1) == 0x80000000);

    // for SHL/SHR, OF is only defined for shift amount of 1
    case CF_SHL_BYTE:
      return ((X86_CTXT(flagsSrc1) & 0x80) != 0) ^ ((X86_CTXT(flagsSrc1) & 0x40) != 0);
    case CF_SHL_WORD:
      return ((X86_CTXT(flagsSrc1) & 0x8000) != 0) ^ ((X86_CTXT(flagsSrc1) & 0x4000) != 0);
    case CF_SHL_DWORD:
      return ((X86_CTXT(flagsSrc1) & 0x80000000) != 0) ^ ((X86_CTXT(flagsSrc1) & 0x40000000) != 0);
    case CF_SHR_BYTE:
      return ((X86_CTXT(flagsSrc1) & 0x80) != 0);
    case CF_SHR_WORD:
      return ((X86_CTXT(flagsSrc1) & 0x8000) != 0);
    case CF_SHR_DWORD:
      return ((X86_CTXT(flagsSrc1) & 0x80000000) != 0);

    // for SAR, OF is always 0
    case CF_SAR_BYTE:
    case CF_SAR_WORD:
    case CF_SAR_DWORD:
      return 0;

    case CF_OR_BYTE:
    case CF_OR_WORD:
    case CF_OR_DWORD:
    case CF_AND_BYTE:
    case CF_AND_WORD:
    case CF_AND_DWORD:
    case CF_XOR_BYTE:
    case CF_XOR_WORD:
    case CF_XOR_DWORD:
    case CF_BCD:
      return 0;

    default:
      ASSERT(UNREACHED);
      return 0;
  }
}

// the aux flag is practically never used, but thankfully it is fairly trivial
// to compute its value
IVAL getAuxFlag() {
  switch (X86_CTXT(flagsOp)) {
    case CF_NOT_LAZY:
    case CF_BCD:
      return X86_CTXT(flagsSrc1) & EFLAGS_BIT_A;
    case CF_ADD_BYTE:
    case CF_ADD_WORD:
    case CF_ADD_DWORD:
      return (((X86_CTXT(flagsSrc1) & 0xF) + (X86_CTXT(flagsSrc2) & 0xF)) & 0x10) != 0;
    case CF_ADC_BYTE:
    case CF_ADC_WORD:
    case CF_ADC_DWORD:
      return (((X86_CTXT(flagsSrc1) & 0xF) + (X86_CTXT(flagsSrc2) & 0xF) + (X86_CTXT(flagsSrc3) != 0)) & 0x10) != 0;
    case CF_SBB_BYTE:
    case CF_SBB_WORD:
    case CF_SBB_DWORD:
      return (((X86_CTXT(flagsSrc1) & 0xF) - (X86_CTXT(flagsSrc2) & 0xF) - (X86_CTXT(flagsSrc3) != 0)) & 0x10) != 0;
    case CF_SUB_BYTE:
    case CF_SUB_WORD:
    case CF_SUB_DWORD:
      return (((X86_CTXT(flagsSrc1) & 0xF) - (X86_CTXT(flagsSrc2) & 0xF)) & 0x10) != 0;

    case CF_INC_BYTE:
    case CF_INC_WORD:
    case CF_INC_DWORD:
      return (X86_CTXT(flagsSrc1) & 0xF) == 0xF;

    case CF_DEC_BYTE:
    case CF_DEC_WORD:
    case CF_DEC_DWORD:
      return (X86_CTXT(flagsSrc1) & 0xF) == 0x0;

    case CF_NEG_BYTE:
    case CF_NEG_WORD:
    case CF_NEG_DWORD:
      return (X86_CTXT(flagsSrc1) & 0xF) != 0x0;

    case CF_OR_BYTE:
    case CF_OR_WORD:
    case CF_OR_DWORD:
    case CF_AND_BYTE:
    case CF_AND_WORD:
    case CF_AND_DWORD:
    case CF_XOR_BYTE:
    case CF_XOR_WORD:
    case CF_XOR_DWORD:
    case CF_SHL_BYTE:
    case CF_SHL_WORD:
    case CF_SHL_DWORD:
    case CF_SHR_BYTE:
    case CF_SHR_WORD:
    case CF_SHR_DWORD:
    case CF_SAR_BYTE:
    case CF_SAR_WORD:
    case CF_SAR_DWORD:
      return 0;

    default:
      ASSERT(UNREACHED);
      return 0;
  }
}

// the parity flag is practically never used, so here we choose to keep it simple
IVAL getParityFlag() {
  IVAL tmp;
  if (X86_CTXT(flagsOp) == CF_NOT_LAZY) {
    return (X86_CTXT(flagsSrc1) & EFLAGS_BIT_P) != 0;
  } else {
    tmp = computeFlagsResultValue();
    return ParityTable[tmp & 0xFF];
  }
}

// the sign flag is used moderately, but mainly in conditional evaluations
IVAL getSignFlag() {
  IVAL res;
  switch (X86_CTXT(flagsOp) & CF_SZ_MASK) {
    case CF_SZ_SPEC(0):
      return (X86_CTXT(flagsSrc1) & EFLAGS_BIT_S) != 0;
    case CF_SZ_SPEC(1):
      res = computeFlagsResultValue();
      return (res & 0x80) != 0;
    case CF_SZ_SPEC(2):
      res = computeFlagsResultValue();
      return (res & 0x8000) != 0;
    case CF_SZ_SPEC(3):
      res = computeFlagsResultValue();
      return (res & 0x80000000) != 0;
    default:
      ASSERT(UNREACHED);
      return 0;
  } 
}

// The zero flag is used commonly, but also mainly in conditional evaluations
// Optimize for common inputs to conditional evaluations
IVAL getZeroFlag() {
  switch (X86_CTXT(flagsOp)) {
    case CF_NOT_LAZY:
      return (X86_CTXT(flagsSrc1) & EFLAGS_BIT_Z) != 0;
    case CF_SUB_BYTE:
    case CF_XOR_BYTE:
      return ((UINT8)X86_CTXT(flagsSrc1)) == ((UINT8)X86_CTXT(flagsSrc2));
    case CF_SUB_WORD:
    case CF_XOR_WORD:
      return ((UINT16)X86_CTXT(flagsSrc1)) == ((UINT16)X86_CTXT(flagsSrc2));
    case CF_SUB_DWORD:
    case CF_XOR_DWORD:
      return ((UINT32)X86_CTXT(flagsSrc1)) == ((UINT32)X86_CTXT(flagsSrc2));
    case CF_AND_BYTE:
      return ((UINT8)(X86_CTXT(flagsSrc1) & X86_CTXT(flagsSrc2))) == 0;
    case CF_AND_WORD:
      return ((UINT16)(X86_CTXT(flagsSrc1) & X86_CTXT(flagsSrc2))) == 0;
    case CF_AND_DWORD:
      return ((UINT32)(X86_CTXT(flagsSrc1) & X86_CTXT(flagsSrc2))) == 0;
    default:
      return !computeFlagsResultValue();
  }
}

// More complex conditions may require looking at multiple flags...
// but frequently these relative comparisons are the result of a comparison
// so special-case comparison results

IVAL cond_eval_be() {
  switch (X86_CTXT(flagsOp)) {
    case CF_NOT_LAZY:
      return (X86_CTXT(flagsSrc1) & (EFLAGS_BIT_C | EFLAGS_BIT_Z)) != 0;
    case CF_SUB_BYTE:
      return ((UINT8)X86_CTXT(flagsSrc1)) <= ((UINT8)X86_CTXT(flagsSrc2));
    case CF_SUB_WORD:
      return ((UINT16)X86_CTXT(flagsSrc1)) <= ((UINT16)X86_CTXT(flagsSrc2));
    case CF_SUB_DWORD:
      return ((UINT32)X86_CTXT(flagsSrc1)) <= ((UINT32)X86_CTXT(flagsSrc2));
    default:
      return getCarryFlag() || getZeroFlag();
  }
}

IVAL cond_eval_l() {
  switch (X86_CTXT(flagsOp)) {
    case CF_NOT_LAZY:
      return (((X86_CTXT(flagsSrc1) & EFLAGS_BIT_S) != 0) ^ ((X86_CTXT(flagsSrc1) & EFLAGS_BIT_O) != 0));
    case CF_SUB_BYTE:
      return ((INT8)X86_CTXT(flagsSrc1)) < ((INT8)X86_CTXT(flagsSrc2));
    case CF_SUB_WORD:
      return ((INT16)X86_CTXT(flagsSrc1)) < ((INT16)X86_CTXT(flagsSrc2));
    case CF_SUB_DWORD:
      return ((INT32)X86_CTXT(flagsSrc1)) < ((INT32)X86_CTXT(flagsSrc2));
    default:
      return getSignFlag() ^ getOverflowFlag();
  }
}

IVAL cond_eval_le() {
  switch (X86_CTXT(flagsOp)) {
    case CF_NOT_LAZY:
      return (((X86_CTXT(flagsSrc1) & EFLAGS_BIT_S) != 0) ^ ((X86_CTXT(flagsSrc1) & EFLAGS_BIT_O) != 0)) || (X86_CTXT(flagsSrc1) & EFLAGS_BIT_Z);
    case CF_SUB_BYTE:
      return ((INT8)X86_CTXT(flagsSrc1)) <= ((INT8)X86_CTXT(flagsSrc2));
    case CF_SUB_WORD:
      return ((INT16)X86_CTXT(flagsSrc1)) <= ((INT16)X86_CTXT(flagsSrc2));
    case CF_SUB_DWORD:
      return ((INT32)X86_CTXT(flagsSrc1)) <= ((INT32)X86_CTXT(flagsSrc2));
    default:
      return (getSignFlag() ^ getOverflowFlag()) || getZeroFlag();
  }
}

// this could probably be done in a more efficient way by reducing redundancies, but it is assumed
// that this is not the common case so it is okay to be slow
void forceEvaluateFlags(INT bits) {
  INT tmp;

  // if already not-lazy then don't bother doing work here
  if (X86_CTXT(flagsOp) == CF_NOT_LAZY)
  {
    // any bits not specified should be zero
    X86_CTXT(flagsSrc1) &= bits;
    return;
  }
  tmp = 0;

  // evaluate all requested flags
  if ((bits & EFLAGS_BIT_C) && getCarryFlag())
    tmp |= EFLAGS_BIT_C;
  if ((bits & EFLAGS_BIT_O) && getOverflowFlag())
    tmp |= EFLAGS_BIT_O;
  if ((bits & EFLAGS_BIT_A) && getAuxFlag())
    tmp |= EFLAGS_BIT_A;
  if ((bits & EFLAGS_BIT_P) && getParityFlag())
    tmp |= EFLAGS_BIT_P;
  if ((bits & EFLAGS_BIT_S) && getSignFlag())
    tmp |= EFLAGS_BIT_S;
  if ((bits & EFLAGS_BIT_Z) && getZeroFlag())
    tmp |= EFLAGS_BIT_Z;
  X86_CTXT(flagsOp) = CF_NOT_LAZY;
  X86_CTXT(flagsSrc1) = tmp;
}

IVAL getFlags() {
  forceEvaluateFlags(EFLAGS_BIT_C | EFLAGS_BIT_O | EFLAGS_BIT_A | EFLAGS_BIT_P | EFLAGS_BIT_S | EFLAGS_BIT_Z);
  return (X86_CTXT(eflags) & ~(EFLAGS_BIT_C | EFLAGS_BIT_O | EFLAGS_BIT_A | EFLAGS_BIT_P | EFLAGS_BIT_S | EFLAGS_BIT_Z)) | X86_CTXT(flagsSrc1);
}

void setFlags(IVAL f) {
  X86_CTXT(eflags) = f;
  SET_FLAGS_EAGER(f);
}

IVAL getDirectionFlag() {
  return (X86_CTXT(eflags) & EFLAGS_BIT_D) != 0;
}

//
// Opcode implementation below this point
//

// carry-bit manipulation is costly but probably rare
// an alternative lazy-flags scheme with multiple levels of masks would probably be slower overall
// even if it would make situations like this one faster
void CMC() {
  forceEvaluateFlags(EFLAGS_BIT_C | EFLAGS_BIT_O | EFLAGS_BIT_A | EFLAGS_BIT_P | EFLAGS_BIT_S | EFLAGS_BIT_Z);
  X86_CTXT(flagsSrc1) ^= EFLAGS_BIT_C;
}

void CLC() {
  forceEvaluateFlags(EFLAGS_BIT_O | EFLAGS_BIT_A | EFLAGS_BIT_P | EFLAGS_BIT_S | EFLAGS_BIT_Z);
}

void STC() {
  forceEvaluateFlags(EFLAGS_BIT_O | EFLAGS_BIT_A | EFLAGS_BIT_P | EFLAGS_BIT_S | EFLAGS_BIT_Z);
  X86_CTXT(flagsSrc1) |= EFLAGS_BIT_C;
}

void SALC() {
  IVAL res;
  res = getCarryFlag() ? 0xFF : 0x00;
  STORE_REG_ID_BYTE(AL,res);
}

void SAHF() {
  IVAL tmp, carry;
  tmp = (LOAD_REG_EAX >> 8) & (EFLAGS_BIT_C | EFLAGS_BIT_A | EFLAGS_BIT_P | EFLAGS_BIT_S | EFLAGS_BIT_Z);
  carry = getOverflowFlag();
  tmp |= carry ? EFLAGS_BIT_O : 0;
  X86_CTXT(flagsOp) = CF_NOT_LAZY;
  X86_CTXT(flagsSrc1) = tmp;
}

void LAHF() {
  IVAL res;
  forceEvaluateFlags(EFLAGS_BIT_C | EFLAGS_BIT_O | EFLAGS_BIT_A | EFLAGS_BIT_P | EFLAGS_BIT_S | EFLAGS_BIT_Z);
  res = X86_CTXT(flagsSrc1) | 0x2;
  STORE_REG_ID_BYTE(AH, res);
}

// The direction flag is not lazy since no benefit to doing so.
void CLD() {
  X86_CTXT(eflags) &= ~EFLAGS_BIT_D;
}

void STD() {
  X86_CTXT(eflags) |= EFLAGS_BIT_D;
}
